package com.masonluo.mlonlinejudge.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.masonluo.mlonlinejudge.dao.*;
import com.masonluo.mlonlinejudge.entity.*;
import com.masonluo.mlonlinejudge.exceptions.ResourceExistException;
import com.masonluo.mlonlinejudge.exceptions.ResourceNotFoundException;
import com.masonluo.mlonlinejudge.exceptions.ServerProcessException;
import com.masonluo.mlonlinejudge.mapper.IoSampleMapper;
import com.masonluo.mlonlinejudge.mapper.ProblemMapper;
import com.masonluo.mlonlinejudge.mapper.TagMapper;
import com.masonluo.mlonlinejudge.model.Pair;
import com.masonluo.mlonlinejudge.model.TestCase;
import com.masonluo.mlonlinejudge.model.TestCaseInfo;
import com.masonluo.mlonlinejudge.model.bo.IoSampleBo;
import com.masonluo.mlonlinejudge.model.bo.ProblemBo;

import com.masonluo.mlonlinejudge.model.bo.TagBo;
import com.masonluo.mlonlinejudge.model.dto.*;
import com.masonluo.mlonlinejudge.model.param.ProblemParam;
import com.masonluo.mlonlinejudge.model.param.SelectProblemParam;
import com.masonluo.mlonlinejudge.model.vo.ProblemNameAndIdVo;
import com.masonluo.mlonlinejudge.model.vo.TagVo;
import com.masonluo.mlonlinejudge.service.*;
import com.masonluo.mlonlinejudge.utils.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 问题模块，将问题进行上传等操作
 *
 * @author masonluo
 * @date 2021/1/22 4:55 下午
 */
@Service
public class ProblemServiceImpl implements ProblemService {

    private final String testCaseDir;

    private final String tmpDir;

    private final SelectProblemService selectProblemService;

    private final ProblemRepository problemRepository;

    private final ProblemTagRepository problemTagRepository;

    private final IoSampleRepository ioSampleRepository;

    private final IndexProblemRepository indexProblemRepository;

    private final ProblemMapper problemMapper;

    private final UserRoleRepository userRoleRepository;

    private final TagMapper tagMapper;

    private final TagRepository tagRepository;

    private final TagService tagService;

    private final RoleService roleService;

    private final IoSampleService ioSampleService;

    private final IoSampleMapper ioSampleMapper;

    private final ObjectMapper objectMapper;

    private final TestcaseFileService testcaseFileService;

    private static final int IN_MASK = 1;

    private static final int OUT_MASK = 1 << 1;

    private static final int RES = IN_MASK | OUT_MASK;

    private static final int _1MB = 1024 * 1024;

    private List allProblems = null;



    public ProblemServiceImpl(SelectProblemService selectProblemService, ProblemRepository problemRepository,
                              ProblemTagRepository problemTagRepository,
                              IoSampleRepository ioSampleRepository,
                              IndexProblemRepository indexProblemRepository,
                              ProblemMapper problemMapper,
                              UserRoleRepository userRoleRepository, TagMapper tagMapper,
                              TagRepository tagRepository, IoSampleService ioSampleService,
                              IoSampleMapper ioSampleMapper,
                              @Qualifier("testCaseDir") String testCaseDir,
                              @Qualifier("tmpDir") String tmpDir, TagService tagService, UserService userService, RoleService roleService, ObjectMapper objectMapper, TestcaseFileService testcaseFileService) {
        this.selectProblemService = selectProblemService;
        this.ioSampleRepository = ioSampleRepository;
        this.indexProblemRepository = indexProblemRepository;
        this.userRoleRepository = userRoleRepository;

        this.tagRepository = tagRepository;
        this.tmpDir = tmpDir;
        this.problemRepository = problemRepository;
        this.problemTagRepository = problemTagRepository;
        this.problemMapper = problemMapper;
        this.tagMapper = tagMapper;
        this.ioSampleService = ioSampleService;
        this.testCaseDir = testCaseDir;
        this.ioSampleMapper = ioSampleMapper;
        this.tagService = tagService;
        this.roleService = roleService;
        this.objectMapper = objectMapper;
        //this.allProblems = InitListOfAllProblems();
        this.testcaseFileService = testcaseFileService;
    }

    @Override
    public int countByVar(String tag, String title, Integer type){
        if (type == null){
            if(tag == null &&  title == null){
                return countAll()+ selectProblemService.countAll();
            }else {
                return selectProblemService.countByAll(tag, title, type) + problemRepository.countByTagAndTitle(tag,title);
            }
        }else if (type == 0 || type == 1){
            return selectProblemService.countByAll(tag, title, type);
        }else if (type == 2) {
            return problemRepository.countByTagAndTitle(tag,title);
        }
        return 0;
    }

    @Override
    public int countAll() {
        return problemRepository.countAll();
    }

    @Override
    public int countByDifficulty(String difficulty) {
        Integer level = 0;
        String difficultyUpper = difficulty.toUpperCase();
        switch (difficultyUpper){
            case "EASY" : level = 0; break;
            case "MEDIUM" : level = 1;break;
            case "HARD" : level = 2;break;
            case "INSUPERABLE" : level = 3;break;
            case "HELL" : level = 4;break;
            default:
                level = 0;
        }
        return problemRepository.countByLevel(level);
    }

    @Override
    public int countByIndexId(Integer indexId) {
        return 0;
    }

    @Override
    public int countByUserId(Integer userId) {
        return 0;
    }

    @Override
    public boolean exist(Integer id) {
        return problemRepository.countById(id) > 0;
    }

    /**
     * 根据问题的id来查询问题
     *  @param id           问题id
     * @param withTag      是否包含问题的tag
     * @param withIoSample 是否包含问题的输入输出样例
     * @return
     */
    @Override
    public ProblemParam findById(Integer id, boolean withTag, boolean withIoSample) {
        Problem problem = problemRepository.selectById(id);
        if (problem == null) {
            throw new ResourceExistException(String.format("当前题目不存在"));
        }
        ProblemParam problemParam = problemMapper.doToParam(problem);
        if (withTag) {
            problemParam.setTags(findTagVoByProblemId(id));
        }
        if (withIoSample) {
            problemParam.setIoSamples(ioSampleService.findByProblemId(id));
        }
        problemParam.setMemoryLimit(problemParam.getMemoryLimit()/_1MB);
        return problemParam;
    }

    @Override
    public List<Integer> getAllProblemId(Integer pageSize, Integer pageNum) {
        pageNum = (pageNum - 1) * pageSize;
        return problemRepository.getAllProblemId(pageSize, pageNum);
    }

    @Override
    public ProblemParam findById(Integer id) {
        return findById(id, false, false);
    }


    @Override
    public List<ProblemParam> listByIds(List<Integer> ids) {
        if (ObjectUtils.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<Problem> problems = problemRepository.listByIds(ids);
        List<ProblemParam> params = problemMapper.doToParam(problems);
        for (ProblemParam problemParam:params){
            problemParam.setTags(findTagVoByProblemId(problemParam.getId()));
            problemParam.setIoSamples(ioSampleService.findByProblemId(problemParam.getId()));
            problemParam.setMemoryLimit(problemParam.getMemoryLimit()/_1MB);
        }
        return params;
    }

    @Override
    public List<ProblemBo> findByTagName(String tagName) {
        List<Problem> res = problemTagRepository.findProblemByTagName(tagName);
        if (res == null) {
            return Collections.emptyList();
        }
        List<ProblemBo> problemBos = problemMapper.to(res);
        problemBos.forEach(problemBo -> {
            problemBo.setMemoryLimit(problemBo.getMemoryLimit()/_1MB);
        });
        return problemBos;
    }

    @Override
    public List<TagBo> findTagByProblemId(Integer id) {
        List<Tag> res = problemTagRepository.findTagByProblemId(id);
        if (res == null) {
            return Collections.emptyList();
        }
        return tagMapper.to(res);
    }

    @Override
    public List<TagVo> findTagVoByProblemId(Integer id) {
        List<Tag> res = problemTagRepository.findTagByProblemId(id);
        if (res == null) {
            return Collections.emptyList();
        }
        return tagMapper.doToVo(res);
    }

    @Override
    public ProblemParam uploadProblem(ProblemUploadDto problem) {
        List<TagBo> tags = problem.getTags();
        System.out.println(tags);
        List<IoSampleBo> ioSamples = problem.getIoSamples();
        // 转换成对应的实体对象
        Problem problemDo = problemMapper.problemUploadDtoToProblem(problem);
        problemDo.setMemoryLimit(problemDo.getMemoryLimit() * _1MB);
        //problemDo.setUserId(problem.getUserId());
        // 保存对应的数据
        problemRepository.insert(problemDo);
        // 对标签等关系进行相应的关联
        doProblemRelatedTag(tags, problemDo);
        // 关联相应的输入输出样例
        doProblemRelatedIoSample(ioSamples, problemDo);
        ProblemParam dto = problemMapper.doToParam(problemDo);
        ProblemParam problemParam = findById(dto.getId(), true, true);
        return problemParam;
    }

    /**
     * 将问题和输入输出进行关联
     */
    private void doProblemRelatedIoSample(List<IoSampleBo> ioSamples, Problem problemDo) {
        if (ObjectUtils.isEmpty(ioSamples)) {
            return;
        }
        List<IoSample> ioSamplesDo = ioSampleMapper.from(ioSamples);
        for (IoSample ioSample : ioSamplesDo) {
            ioSample.setProblemId(problemDo.getId());
        }
        ioSampleRepository.insertAll(ioSamplesDo);
    }

    /**
     * 将标签和问题进行一个关联
     */
    private void doProblemRelatedTag(List<TagBo> tags, Problem problemDo) {
        if (ObjectUtils.isEmpty(tags)) {
            return;
        }

        //List<ProblemTag> problemTags = new ArrayList<>();
        for (TagBo tag : tags) {
            Tag addTag = tagService.addTag(tag.getName());
            TagBo tagBo = tagMapper.to(addTag);
            ProblemTag problemTag = new ProblemTag();
            problemTag.setProblemId(problemDo.getId());
            problemTag.setTagId(tagBo.getId());
            problemTag.setType(2);
            //problemTags.add(problemTag);
            problemTagRepository.insert(problemTag);
        }
        //problemTagRepository.insertAll(problemTags);
    }

    @Override
    public TestCaseUploadDto uploadTestCase(MultipartFile zipFile) {
        try {
            TestCaseUploadDto uploadDto;
            // 生成测试数据id，作为测试数据的文件夹名称
            String testCaseId = UUIDUtils.getUuid();
            // 将文件上传到临时目录中
            // 生成父目录
            File parentDir = new File(tmpDir + "/" + testCaseId);
            if (!parentDir.mkdirs()) {
                throw new ServerProcessException("Can't create a new directory");
            }
            String filename = zipFile.getOriginalFilename();
            if (StringUtils.isBlank(filename)) {
                filename = "target.zip";
            }
            File target = new File(parentDir, filename);
            String targetPath = target.getAbsolutePath();
            zipFile.transferTo(target);
            // 进行解压到当前目录上
            ZipUtils.unzip(targetPath);
            // 删除原来的文件
            if (!target.delete()) {
                throw new ServerProcessException("Can't delete file in the path, does not has the right");
            }
            // 验证上传文件的格式，判断是否是正确的命名
            verifyTestCasePattern(parentDir);
            // 生成info实体
            TestCaseInfo info = generateTestCaseInfo(parentDir);
            // 保存info文件
            generateInfoFile(info, parentDir);
            // 将该目录移动到test_case的目录下面
            moveDirectory(parentDir, testCaseDir);
            // 生成即将要返回的信息
            uploadDto = generateTestCaseUploadDto(testCaseId, info);
            return uploadDto;
        } catch (IOException e) {
            throw new ServerProcessException("Can't save the zip file in disk", e);
        }
    }

    @Override
    public List<ProblemNameAndIdVo> findProblemByIndexId(Integer indexId) {
        List<ProblemNameAndIdVo> res = indexProblemRepository.findProblemByIndexId(indexId);
        return res == null ? Collections.emptyList() : res;
    }

    @Override
    public List<ProblemParam> list(Integer pageNum, Integer pageSize) {
        IPage<Problem> page = new Page<>(pageNum, pageSize);
        IPage<Problem>problemIPage = problemRepository.selectPage(page, null);
        List<ProblemParam> problems = sorter(problemIPage, pageNum);
        return problems;
    }

    /**
    * @Description: 对查询的题目进行分页
    * @Author: LOTP
    * @Date: 2021/11/9
    */
    public  List<ProblemParam> sorter(IPage<Problem> problemIPage,Integer pageNum){
        if(problemIPage.getRecords().size() != 0 ){
            List<ProblemParam> problems = new ArrayList<>();
            if (!CollectionUtils.isEmpty(problemIPage.getRecords())){
                problems = problemIPage.getRecords().stream().map(problemMapper::doToParam).collect(Collectors.toList());
            }
            for (ProblemParam problemParam:problems){
                problemParam.setTags(findTagVoByProblemId(problemParam.getId()));
                problemParam.setIoSamples(ioSampleService.findByProblemId(problemParam.getId()));
                problemParam.setMemoryLimit(problemParam.getMemoryLimit()/_1MB);
            }
            return  problems;
        }else{
            if(pageNum == 1){
                throw new ResourceNotFoundException(String.format("无该题目数据"));
            }else {
                throw new ResourceNotFoundException(String.format("当前页不存在"));
            }
        }
    }

    @Override
    public <T> List<T> searchAllProblems(String tag, String title, Integer type, Integer pageNum, Integer pageSize){
        //题目类型(整型) 0代表选择题，1代表判断题，2代表程序题
        if (type == null) {
            if (tag == null && title == null) {
                return listAllProblem(pageNum, pageSize);
            } else {
                List<SelectProblemParam> selectProblems = selectProblemService.searchByTagAndTitle(tag, title);
                List<ProblemParam> problems = searchByTagAndTitle(tag, title);
                List finalProblems = new ArrayList();
                finalProblems.addAll(selectProblems);
                finalProblems.addAll(problems);
                if ((pageNum * pageSize - finalProblems.size() >= pageSize) && ((pageNum - 1) * pageSize - finalProblems.size() <= pageSize)) {
                    if (pageNum == 1){
                        throw new ResourceNotFoundException(String.format("没有该题数据"));
                    }else {
                        throw new ResourceNotFoundException(String.format("当前页不存在"));
                    }
                } else {
                    IPage iPage = new Page<>();
                    iPage.setRecords(finalProblems);
                    //iPage.setTotal(finalProblems.size());
                    int startIndex = (pageNum - 1) * pageSize;
                    int endIndex = Math.min(pageNum * pageSize, finalProblems.size());
                    List list = iPage.getRecords().subList(startIndex, endIndex);
                    return list;
                }
            }
        }else {
            if (type == 0 || type == 1){
                List<SelectProblemParam> selectProblems = selectProblemService.searchProblems(tag, title, type, pageNum, pageSize);
                return (List<T>) selectProblems;
            }else if (type == 2) {
                List<ProblemParam> problems = searchProblems(tag, title, pageNum, pageSize);
                return (List<T>) problems;
            }
        }
        throw new ResourceNotFoundException(String.format("没有此类型的题目"));
    }

    @Override
    public  List<ProblemParam> searchProblems(String tag, String title, Integer pageNum, Integer pageSize) {
        IPage<Problem> page = new Page<>(pageNum, pageSize);
        IPage<Problem> problemIPage = problemRepository.searchProblem(page, tag, title);
        List<ProblemParam> problems = sorter(problemIPage, pageNum);
        return problems;
    }

    @Override
    public List<ProblemParam> searchByTagAndTitle(String tag, String title){
        List<Problem> problems = problemRepository.searchByTagAndTitle(tag, title);
        List<ProblemParam> problemParams = problemMapper.doToParam(problems);
        for (ProblemParam problemParam:problemParams){
            problemParam.setTags(findTagVoByProblemId(problemParam.getId()));
            problemParam.setIoSamples(ioSampleService.findByProblemId(problemParam.getId()));
            problemParam.setMemoryLimit(problemParam.getMemoryLimit()/_1MB);
        }
        return problemParams;
    }

    @Override
    public List<ProblemParam> listAllProblemByDifficulty(String difficulty, Integer pageNum, Integer pageSize) {
        Integer level = 0;
        String difficultyUpper = difficulty.toUpperCase();
        switch (difficultyUpper){
            case "EASY" : level = 0; break;
            case "MEDIUM" : level = 1;break;
            case "HARD" : level = 2;break;
            case "INSUPERABLE" : level = 3;break;
            case "HELL" : level = 4;break;
            default:
                level = 0;
        }
        IPage<Problem> page = new Page<>(pageNum, pageSize);
        IPage<Problem> problemIPage = problemRepository.searchProblemByLevel(page,level);
        List<ProblemParam> problems = sorter(problemIPage, pageNum);
        return problems;
    }

    @Override
    public List listAllProblem(Integer pageNum, Integer pageSize){
        /*List<SelectProblemParam> selectProblems = selectProblemService.getSelectProblem();
        List<Problem> problems = problemRepository.listAll();*/
        List finalProblems = new ArrayList();
        List<Integer> allProblemId = getAllProblemId(pageSize, pageNum);
        int total = countAll();
        int dif = pageNum * pageSize - total;
        if (dif <= 0){
            List<ProblemParam> problems = listByIds(allProblemId);
            return problems;
            //finalProblems.addAll(problems);
        }else if (dif > 0 && dif < pageSize){
            for (int i = 0; i < (pageSize - dif); i++){
                ProblemParam problem = findById(allProblemId.get(i), true, true);
                finalProblems.add(problem);
            }

            for (int i = (pageSize - dif); i < allProblemId.size(); i++){
                SelectProblemParam selectProblem = selectProblemService.getSelectProblemById(allProblemId.get(i));
                finalProblems.add(selectProblem);
            }
        }else if (dif >= pageSize){
            for (int i = 0; i < allProblemId.size(); i++){
                SelectProblemParam selectProblem= selectProblemService.getSelectProblemById(allProblemId.get(i));
                finalProblems.add(selectProblem);
            }

        }

        return finalProblems;

        /*for (Problem problem: problems){
            ProblemParam problemParam = problemMapper.doToParam(problem);
            problemParam.setTags(findTagVoByProblemId(problemParam.getId()));
            problemParam.setIoSamples(ioSampleService.findByProblemId(problemParam.getId()));
            problemParam.setMemoryLimit(problemParam.getMemoryLimit()/_1MB);
            finalProblems.add(problemParam);
        }
        for (SelectProblemParam selectProblem:selectProblems){
            finalProblems.add(selectProblem);
        }
        if ((pageNum * pageSize - finalProblems.size() >= pageSize) && ((pageNum-1) * pageSize - finalProblems.size() <= pageSize)){
            throw new ResourceNotFoundException(String.format("当前页不存在"));
        }else {
            IPage iPage = new Page<>();
            iPage.setRecords(finalProblems);
            //iPage.setTotal(finalProblems.size());
            int startIndex=(pageNum - 1) * pageSize;
            int endIndex= Math.min(pageNum * pageSize, finalProblems.size());
            List list = iPage.getRecords().subList(startIndex, endIndex);
            return list;
        }*/
    }

    @Override
    public List<ProblemParam> listByUserId(Integer userId, Integer pageNum, Integer pageSize) {
        IPage<Problem> page = new Page<>(pageNum, pageSize);
        QueryWrapper<Problem> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",userId);
        IPage<Problem> problemIPage = problemRepository.selectPage(page, wrapper);
        if (!CollectionUtils.isEmpty(problemIPage.getRecords())){
            List<ProblemParam> problems = new ArrayList<>();
            problems = (List<ProblemParam>) page.getRecords().stream().map(problem -> {
                return problemMapper.doToParam(problem);
            }).collect(Collectors.toList());
            problems.forEach(problemParam -> {
                problemParam.setMemoryLimit(problemParam.getMemoryLimit()/_1MB);
            });
            return problems;
        } else {
            throw new ResourceNotFoundException(String.format("The userId [%d] does not exist.", userId));
        }
    }

    @Override
    public void delete(Integer problemId) throws Exception {
        /*if (problemRepository.deleteById(problemId) > 0){
            this.allProblems = this.InitListOfAllProblems();
        }*/
        ProblemParam bo = this.findById(problemId);
        String testCaseId = bo.getTestCaseId();
        problemRepository.deleteById(problemId);
        problemTagRepository.deleteByProblemId(problemId,2);
        ioSampleRepository.deleteByProblemId(problemId);
        testcaseFileService.deleteTestCase(testCaseId);

    }

    @Override
    public ProblemParam updateProblemById(ProblemBo problemBo, Integer userId, Integer problemId) {
        problemBo.setId(problemId);
        ProblemParam bo = this.findById(problemBo.getId());
        if (bo == null ) {
            throw new ResourceExistException(String.format("当前问题不存在"));
        }else {

            if (!Objects.equals(bo.getUserId(), userId)){
                String role = roleService.findByUserId(userId).getName();
                if (!"superAdmin".equals(role)){
                    throw new ResourceExistException(String.format("您不是超级管理员或该题目的创建者，无权修改题目"));
                }
            }

            //修改前该问题的所有输入输出信息
            List<IoSample> oldIoSamples = ioSampleRepository.findByProblemId(problemBo.getId());
            //修改后该问题的所有输入输出信息
            List<IoSampleBo> newIoSamples = problemBo.getIoSamples();
            List<TagBo> newTags = problemBo.getTags();
            problemTagRepository.deleteByProblemId(problemBo.getId(),2);
            doProblemRelatedTag(newTags,problemMapper.from(problemBo));

            //新的输入输出信息可能会比旧的多
            if (newIoSamples.size() > oldIoSamples.size()){
                //将id已存在的部分进行修改
                newIoSamples = updateIoSamples(oldIoSamples,newIoSamples);
                List<IoSample> from = ioSampleMapper.from(newIoSamples);
                for (IoSample ioSample:from){
                    ioSample.setProblemId(problemBo.getId());
                }
                //将新增加的输入输出信息插入到表中
                ioSampleRepository.insertAll(from);
            }else
            //新的输入输出信息可能会比旧的少
            if (newIoSamples.size() < oldIoSamples.size()){
                //先删除多的部分
                int extra = oldIoSamples.size() - newIoSamples.size();
                for (int i = 0 ; i < extra ; i++){
                    System.out.println(oldIoSamples.get(oldIoSamples.size()-1));
                    ioSampleRepository.deleteById(oldIoSamples.get((oldIoSamples.size()-1)).getId());
                    oldIoSamples.remove(oldIoSamples.size()-1);
                }
                //将剩余部分进行修改
                updateIoSamples(oldIoSamples,newIoSamples);
            }else
            if (newIoSamples.size() == oldIoSamples.size()){
                updateIoSamples(oldIoSamples,newIoSamples);
            }
            Problem problem = problemMapper.from( problemBo );
            problem.setUserId(bo.getUserId());
            problem.setMemoryLimit(problem.getMemoryLimit()*_1MB);
            problemRepository.updateById(problem);
        }
        return findById(problemBo.getId(), true, true);
    }



    private List<IoSampleBo> updateIoSamples(List<IoSample> oldIoSamples, List<IoSampleBo> newIoSamples){
        for (IoSample ioSample:oldIoSamples){
            ioSample.setInputSample(newIoSamples.get(0).getInputSample());
            ioSample.setOutputSample(newIoSamples.get(0).getOutputSample());
            ioSampleRepository.updateById(ioSample);
            //System.out.println(ioSample);
            //更新完之后要在列表中删除该项，这样剩下的都是新的输入输出信息
            if (newIoSamples.size() != 0){
                newIoSamples.remove(0);
            }

        }
        return newIoSamples;
    }

    private TestCaseUploadDto generateTestCaseUploadDto(String testCaseId, TestCaseInfo info) {
        TestCaseUploadDto uploadDto = new TestCaseUploadDto();
        uploadDto.setTestCaseId(testCaseId);
        List<Pair<String, String>> testCaseFiles = new ArrayList<>();
        for (Integer idx : info.getTestCase().keySet()) {
            String in = String.format("%d.in", idx);
            String out = String.format("%d.out", idx);
            testCaseFiles.add(new Pair<>(in, out));
        }
        uploadDto.setFiles(testCaseFiles);
        return uploadDto;
    }

    /**
     * 移动文件夹到指定的路径
     */
    private void moveDirectory(File parentDir, String testCaseDir) {
        try {
            Files.move(parentDir.toPath(), Paths.get(testCaseDir + "/" + parentDir.getName()));
        } catch (IOException e) {
            e.printStackTrace();
            throw new ServerProcessException(
                    "Can't move the directory[" +
                            parentDir.getAbsolutePath() + "] to the target path [" + testCaseDir + "]", e);
        }
    }

    /**
     * 将info文件保存到文件夹中
     */
    private void generateInfoFile(TestCaseInfo info, File parentDir) {
        try {
            String infoStr = objectMapper.writeValueAsString(info);
            FileWriter fos = new FileWriter(new File(parentDir, "info"));
            fos.write(infoStr);
            fos.close();
        } catch (IOException e) {
            throw new ServerProcessException("Can't save the info file", e);
        }
    }

    /**
     * 生成info文件实体
     */
    private TestCaseInfo generateTestCaseInfo(File parentDir) {
        TestCaseInfo info = new TestCaseInfo();
        // 目前只提供了非特判
        info.setSpj(false);
        File[] children = parentDir.listFiles();
        if (ObjectUtils.isEmpty(children)) {
            return info;
        }
        Map<Integer, TestCase> testCaseMap = info.getTestCase();
        for (File child : children) {
            String name = child.getName();
            int idx = Integer.parseInt(name.substring(0, name.lastIndexOf(".")));
            String suffix = name.substring(name.lastIndexOf(".") + 1);
            TestCase testCase = null;
            if ((testCase = testCaseMap.get(idx)) == null) {
                testCase = new TestCase();
            }
            if (ObjectUtils.nullSafeEquals(suffix, "in")) {
                testCase.setInputName(name);
                testCase.setInputSize(child.length());
            } else {
                testCase.setOutputName(name);
                testCase.setOutputSize(child.length());
                testCase.setStrippedOutputMd5(strippedMd5(child));
            }
            testCaseMap.put(idx, testCase);
        }
        return info;
    }

    /**
     * 将文件独取出来，并且用md5生成摘要
     */
    private String strippedMd5(File child) {
        try {
            StringBuilder sb = new StringBuilder();
            BufferedReader br = new BufferedReader(new FileReader(child));
            String line = null;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            String content = sb.toString();
            content = doStripped(content);
            br.close();
            return CodecUtils.md5DigestAsHex(content);
        } catch (IOException e) {
            throw new ServerProcessException("The file [" + child.getName() + "] is not a readable file");
        }
    }

    /**
     * 将所有的空白字符去除
     */
    private String doStripped(String content) {
        if (content == null) {
            return null;
        }
        String res;
        Pattern pattern = Pattern.compile("\\s*|\t|\r|\n");
        Matcher matcher = pattern.matcher(content);
        res = matcher.replaceAll("");
        return res;
    }

    /**
     * 验证压缩包内的文件是否符合格式
     */
    private void verifyTestCasePattern(File parentDir) {
        File[] children = parentDir.listFiles();
        if (ObjectUtils.isEmpty(children)) {
            throw new IllegalArgumentException("Test case should not be empty, please upload at lease a test case");
        }
        if (children.length % 2 != 0) {
            throw new IllegalArgumentException("Each xx.in file should has another xx.out to verify");
        }

        int[] assistArray = new int[children.length / 2];
        for (File child : children) {
            String name = child.getName();
            if (!child.isFile()) {
                throw new IllegalArgumentException("Test case should not be contain a directory");
            }
            if (!verifyFilename(name)) {
                throw new IllegalArgumentException("The test case file name should be match the pattern '[0-9]+\\.(in|out)'");
            }
            // 分割序号和后缀
            int idx = Integer.parseInt(name.substring(0, name.lastIndexOf(".")));
            String suffix = name.substring(name.lastIndexOf(".") + 1);
            if (idx > assistArray.length) {
                throw new IllegalArgumentException("The test case file should be in order");
            }
            if (ObjectUtils.nullSafeEquals(suffix, "in")) {
                assistArray[idx - 1] |= IN_MASK;
            } else {
                assistArray[idx - 1] |= OUT_MASK;
            }
        }
        for (int i : assistArray) {
            if (i != RES) {
                throw new IllegalArgumentException("in and out file should be appear together\n" + changeToStringName(assistArray));
            }
        }
    }

    private boolean verifyFilename(String name) {
        String pattern = "[0-9]+\\.(in|out)";
        return name.matches(pattern);
    }

    private String changeToStringName(int[] assistArray) {
        StringBuilder sb = new StringBuilder();
        sb.append("\tin\tout\n");
        for (int i = 0; i < assistArray.length; i++) {
            sb.append(i + 1);
            sb.append((assistArray[i] & IN_MASK) == 0 ? 0 : 1);
            sb.append("\t");
            sb.append((assistArray[i] & OUT_MASK) == 0 ? 0 : 1);
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        String pattern = "[0-9]+\\.(in|out)";
        System.out.println("1.in".matches(pattern));
        String name = "111.in";
        System.out.println(name.substring(0, name.lastIndexOf(".")));
    }
}
